#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
账单分析程序
支持支付宝CSV和微信Excel格式的流水记录分析
"""

import pandas as pd
import os
import re
from datetime import datetime, timedelta
from typing import Dict, List, Tuple, Optional
from tabulate import tabulate
import warnings
warnings.filterwarnings('ignore')


class BillAnalyzer:
    """账单分析器"""
    
    def __init__(self, bill_folder: str = "zhangdan"):
        self.bill_folder = bill_folder
        self.user_nicknames = {}  # 存储用户昵称
        self.bills_data = []  # 存储处理后的账单数据
        self.field_mapping = self._init_field_mapping()
        
    def _init_field_mapping(self) -> Dict[str, Dict[str, str]]:
        """初始化字段映射关系"""
        return {
            'alipay': {
                '交易时间': 'datetime',
                '交易对方': 'counterparty', 
                '商品说明': 'description',
                '收/支': 'type',
                '金额': 'amount',
                '收/付款方式': 'payment_method',
                '交易状态': 'status',
                '交易分类': 'category',
                '对方账号': 'counterparty_account',
                '备注': 'remark'
            },
            'wechat': {
                '交易时间': 'datetime',
                '交易类型': 'type',
                '交易对方': 'counterparty',
                '商品': 'description', 
                '收/支': 'income_expense',
                '金额(元)': 'amount',
                '支付方式': 'payment_method',
                '当前状态': 'status',
                '交易单号': 'transaction_id',
                '商户单号': 'merchant_id',
                '备注': 'remark'
            }
        }
    
    def load_bills(self) -> None:
        """加载账单文件"""
        if not os.path.exists(self.bill_folder):
            print(f"错误：账单文件夹 '{self.bill_folder}' 不存在")
            return
            
        files = os.listdir(self.bill_folder)
        alipay_files = [f for f in files if f.endswith('.csv')]
        wechat_files = [f for f in files if f.endswith('.xlsx')]
        
        print(f"发现 {len(alipay_files)} 个支付宝文件，{len(wechat_files)} 个微信文件")
        
        # 处理支付宝文件
        for file in alipay_files:
            self._process_alipay_file(file)
            
        # 处理微信文件
        for file in wechat_files:
            self._process_wechat_file(file)
    
    def _process_alipay_file(self, filename: str) -> None:
        """处理支付宝CSV文件"""
        filepath = os.path.join(self.bill_folder, filename)
        print(f"正在处理支付宝文件: {filename}")
        
        try:
            # 先读取文件内容找到数据开始行
            with open(filepath, 'r', encoding='gbk', errors='ignore') as f:
                lines = f.readlines()
            
            # 查找包含"交易时间"的行
            data_start_row = None
            for i, line in enumerate(lines):
                if '交易时间' in line and '交易分类' in line:
                    data_start_row = i
                    break
            
            if data_start_row is None:
                print(f"无法找到数据开始行，跳过文件 {filename}")
                return
            
            print(f"数据从第 {data_start_row + 1} 行开始")
            
            # 提取用户信息和时间范围
            self._extract_file_info(filepath, 'alipay')
            
            # 使用pandas读取数据，跳过前面的行
            df = pd.read_csv(filepath, encoding='gbk', skiprows=data_start_row)
            
            # 处理数据
            processed_data = self._process_alipay_data(df)
            self.bills_data.extend(processed_data)
            
        except Exception as e:
            print(f"处理支付宝文件 {filename} 时出错: {e}")
    
    def _process_wechat_file(self, filename: str) -> None:
        """处理微信Excel文件"""
        filepath = os.path.join(self.bill_folder, filename)
        print(f"正在处理微信文件: {filename}")
        
        try:
            # 读取Excel文件
            df = pd.read_excel(filepath)
            
            # 查找数据开始行
            data_start_row = self._find_data_start_row(df)
            if data_start_row is None:
                print(f"无法找到数据开始行，跳过文件 {filename}")
                return
                
            # 重新读取数据
            df = pd.read_excel(filepath, skiprows=data_start_row)
            
            # 提取用户信息和时间范围
            self._extract_file_info(filepath, 'wechat')
            
            # 处理数据
            processed_data = self._process_wechat_data(df)
            self.bills_data.extend(processed_data)
            
        except Exception as e:
            print(f"处理微信文件 {filename} 时出错: {e}")
    
    def _find_data_start_row(self, df: pd.DataFrame) -> Optional[int]:
        """查找数据开始行"""
        for i, row in df.iterrows():
            # 查找包含"交易时间"或"时间"的行
            row_str = ' '.join([str(cell) for cell in row.values if pd.notna(cell)])
            if '交易时间' in row_str or '时间' in row_str:
                return i
        return None
    
    def _extract_file_info(self, filepath: str, platform: str) -> None:
        """提取文件信息（用户昵称、时间范围等）"""
        try:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
                
            # 提取时间范围
            time_pattern = r'(\d{4}-\d{2}-\d{2})'
            times = re.findall(time_pattern, content)
            if len(times) >= 2:
                start_time = times[0]
                end_time = times[1]
                print(f"时间范围: {start_time} 到 {end_time}")
            
            # 提取用户信息（这里简化处理，实际可能需要更复杂的逻辑）
            user_key = f"{platform}_user"
            if user_key not in self.user_nicknames:
                self.user_nicknames[user_key] = f"用户_{len(self.user_nicknames) + 1}"
                
        except Exception as e:
            print(f"提取文件信息时出错: {e}")
    
    def _process_alipay_data(self, df: pd.DataFrame) -> List[Dict]:
        """处理支付宝数据"""
        processed_data = []
        
        for _, row in df.iterrows():
            if pd.isna(row.get('交易时间')):
                continue
                
            try:
                # 解析时间
                datetime_str = str(row['交易时间'])
                if ' ' in datetime_str:
                    dt = datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
                else:
                    dt = datetime.strptime(datetime_str, '%Y-%m-%d')
                
                # 处理金额
                amount_str = str(row.get('金额', '0')).replace(',', '')
                amount = float(amount_str) if amount_str.replace('.', '').isdigit() else 0.0
                
                # 确定收支类型（支付宝中"不计收支"表示不计收支）
                income_expense = str(row.get('收/支', ''))
                if '不计收支' in income_expense:
                    bill_type = 'neutral'  # 不计收支
                elif '收入' in income_expense or '收款' in income_expense:
                    bill_type = 'income'
                elif '支出' in income_expense or '付款' in income_expense:
                    bill_type = 'expense'
                else:
                    bill_type = 'unknown'
                
                bill_data = {
                    'platform': 'alipay',
                    'datetime': dt,
                    'counterparty': str(row.get('交易对方', '')),
                    'description': str(row.get('商品说明', '')),
                    'type': bill_type,
                    'amount': amount,
                    'payment_method': str(row.get('收/付款方式', '')),
                    'status': str(row.get('交易状态', '')),
                    'original_category': str(row.get('交易分类', '')),  # 保留原始交易分类
                    'remark': str(row.get('备注', '')),
                    'raw_data': row.to_dict()
                }
                
                processed_data.append(bill_data)
                
            except Exception as e:
                print(f"处理支付宝数据行时出错: {e}")
                continue
                
        return processed_data
    
    def _process_wechat_data(self, df: pd.DataFrame) -> List[Dict]:
        """处理微信数据"""
        processed_data = []
        
        for _, row in df.iterrows():
            if pd.isna(row.get('交易时间')):
                continue
                
            try:
                # 解析时间
                datetime_str = str(row['交易时间'])
                if ' ' in datetime_str:
                    dt = datetime.strptime(datetime_str, '%Y-%m-%d %H:%M:%S')
                else:
                    dt = datetime.strptime(datetime_str, '%Y-%m-%d')
                
                # 处理金额
                amount_str = str(row.get('金额(元)', '0')).replace(',', '')
                amount = float(amount_str) if amount_str.replace('.', '').isdigit() else 0.0
                
                # 确定收支类型（微信中"/"表示不计收支）
                income_expense = str(row.get('收/支', ''))
                if income_expense == '/':
                    bill_type = 'neutral'  # 不计收支
                elif '收入' in income_expense or '收款' in income_expense:
                    bill_type = 'income'
                elif '支出' in income_expense or '付款' in income_expense:
                    bill_type = 'expense'
                else:
                    bill_type = 'unknown'
                
                bill_data = {
                    'platform': 'wechat',
                    'datetime': dt,
                    'counterparty': str(row.get('交易对方', '')),
                    'description': str(row.get('商品', '')),
                    'type': bill_type,
                    'amount': amount,
                    'payment_method': str(row.get('支付方式', '')),
                    'status': str(row.get('当前状态', '')),
                    'original_category': str(row.get('交易类型', '')),  # 保留原始交易分类
                    'remark': str(row.get('备注', '')),
                    'raw_data': row.to_dict()
                }
                
                processed_data.append(bill_data)
                
            except Exception as e:
                print(f"处理微信数据行时出错: {e}")
                continue
                
        return processed_data
    
    def classify_bills(self) -> None:
        """对账单进行分类"""
        for bill in self.bills_data:
            # 第一级分类
            bill['category_level1'] = self._classify_level1(bill)
            
            # 第二级分类
            bill['category_level2'] = self._classify_level2(bill)
            
            # 第三级分类
            bill['category_level3'] = self._classify_level3(bill)
            
            # 第四级分类（细项）
            bill['category_level4'] = self._classify_level4(bill)
    
    def _classify_level1(self, bill: Dict) -> str:
        """第一级分类 - 营业收入和营业总支出"""
        description = bill['description'].lower()
        counterparty = bill['counterparty'].lower()
        text_to_check = f"{description} {counterparty}"
        bill_type = bill['type']
        
        # 如果是收入类型，归入营业收入
        if bill_type == 'income':
            return '营业收入'
        
        # 如果是支出类型，归入营业总支出
        elif bill_type == 'expense':
            return '营业总支出'
        
        # 不计收支的情况
        else:
            return '其他'
    
    def _classify_level2(self, bill: Dict) -> str:
        """第二级分类"""
        level1 = bill['category_level1']
        description = bill['description'].lower()
        counterparty = bill['counterparty'].lower()
        text_to_check = f"{description} {counterparty}"
        
        if level1 == '营业收入':
            # 主营业务收入（工资）
            if any(keyword in text_to_check for keyword in ['工资', '薪资', '月薪', '奖金', '绩效']):
                return '主营业务收入'
            # 其他业务收入(兼职)
            elif any(keyword in text_to_check for keyword in ['兼职', '副业', '咨询', '服务']):
                return '其他业务收入'
            # 投资收益
            elif any(keyword in text_to_check for keyword in ['余额宝', '基金', '理财', '投资', '股票', '证券']):
                return '投资收益'
            # 被动收入
            elif any(keyword in text_to_check for keyword in ['租金', '房租', '分红', '股息']):
                return '被动收入'
            # 意外收入
            elif any(keyword in text_to_check for keyword in ['红包', '奖励', '退款', '返还']):
                return '意外收入'
            else:
                return '其他营业收入'
        
        elif level1 == '营业总支出':
            # 管理费用
            if any(keyword in text_to_check for keyword in ['房租', '水费', '电费', '燃气', '手机', '话费', '网络', '宽带', '保险', '医疗', '医院', '税费', '培训', '课程', '学习', '书籍']):
                return '管理费用'
            # 销售费用（个人消费）
            elif any(keyword in text_to_check for keyword in ['外卖', '餐饮', '餐厅', '超市', '购物', '打车', '交通', '地铁', '公交', '加油', '游戏', '电影', '娱乐', '服装', '衣服', '数码', '电子', '旅游', '份子钱', '随礼']):
                return '销售费用'
            # 财务费用（利息/手续费）
            elif any(keyword in text_to_check for keyword in ['利息', '手续费', '财务']):
                return '财务费用'
            # 其他费用
            else:
                return '其他费用'
        
        else:
            return '未分类'
    
    def _classify_level3(self, bill: Dict) -> str:
        """第三级分类"""
        level2 = bill['category_level2']
        description = bill['description'].lower()
        counterparty = bill['counterparty'].lower()
        text_to_check = f"{description} {counterparty}"
        
        if level2 == '管理费用':
            # 房租
            if any(keyword in text_to_check for keyword in ['房租', '租金']):
                return '房租'
            # 水电气费
            elif any(keyword in text_to_check for keyword in ['水费', '电费', '燃气费', '水电']):
                return '水电气费'
            # 网络/手机费
            elif any(keyword in text_to_check for keyword in ['手机', '话费', '网络', '宽带', '通讯']):
                return '网络/手机费'
            # 保险/医疗
            elif any(keyword in text_to_check for keyword in ['保险', '医疗', '医院', '药店', '体检']):
                return '保险/医疗'
            # 税费
            elif any(keyword in text_to_check for keyword in ['税费', '税收', '税务']):
                return '税费'
            # 学习提升
            elif any(keyword in text_to_check for keyword in ['培训', '课程', '学习', '书籍', '教育']):
                return '学习提升'
            else:
                return '其他管理费用'
        
        elif level2 == '销售费用':
            # 食品
            if any(keyword in text_to_check for keyword in ['外卖', '餐饮', '餐厅', '超市', '食品', '零食', '水果']):
                return '食品'
            # 交通
            elif any(keyword in text_to_check for keyword in ['打车', '交通', '地铁', '公交', '加油', '滴滴', '高德']):
                return '交通'
            # 娱乐
            elif any(keyword in text_to_check for keyword in ['游戏', '电影', '娱乐', '视频', '音乐']):
                return '娱乐'
            # 购物
            elif any(keyword in text_to_check for keyword in ['购物', '服装', '衣服', '数码', '电子', '商品']):
                return '购物'
            # 旅游
            elif any(keyword in text_to_check for keyword in ['旅游', '旅行', '酒店', '机票']):
                return '旅游'
            # 人情往来
            elif any(keyword in text_to_check for keyword in ['份子钱', '随礼', '人情', '礼金']):
                return '人情往来'
            else:
                return '其他销售费用'
        
        elif level2 == '财务费用':
            if '利息' in text_to_check:
                return '利息'
            elif '手续费' in text_to_check:
                return '手续费'
            else:
                return '其他财务费用'
        
        elif level2 == '其他费用':
            if any(keyword in text_to_check for keyword in ['随礼', '份子钱']):
                return '随礼/份子钱'
            elif '罚款' in text_to_check:
                return '罚款'
            else:
                return '其他费用'
        
        else:
            return '未分类'
    
    def _classify_level4(self, bill: Dict) -> str:
        """第四级分类（细项）"""
        level1 = bill['category_level1']
        level2 = bill['category_level2']
        level3 = bill['category_level3']
        description = bill['description'].lower()
        counterparty = bill['counterparty'].lower()
        text_to_check = f"{description} {counterparty}"
        
        # 营业收入细项
        if level1 == '营业收入':
            if level2 == '主营业务收入':
                if '工资' in text_to_check or '薪资' in text_to_check or '月薪' in text_to_check:
                    return '工资'
                elif '奖金' in text_to_check or '绩效' in text_to_check:
                    return '奖金'
                else:
                    return '其他工资收入'
            
            elif level2 == '其他业务收入':
                if '兼职' in text_to_check or '副业' in text_to_check:
                    return '兼职收入'
                elif '咨询' in text_to_check or '服务' in text_to_check:
                    return '咨询服务'
                else:
                    return '其他业务收入'
            
            elif level2 == '投资收益':
                if '余额宝' in text_to_check or '基金' in text_to_check:
                    return '理财收益'
                elif '股票' in text_to_check or '证券' in text_to_check:
                    return '股票收益'
                else:
                    return '其他投资收益'
            
            elif level2 == '被动收入':
                if '租金' in text_to_check or '房租' in text_to_check:
                    return '租金收入'
                elif '分红' in text_to_check or '股息' in text_to_check:
                    return '分红收入'
                else:
                    return '其他被动收入'
            
            elif level2 == '意外收入':
                if '红包' in text_to_check or '奖励' in text_to_check:
                    return '红包奖励'
                elif '退款' in text_to_check or '返还' in text_to_check:
                    return '退款返还'
                else:
                    return '其他意外收入'
        
        # 营业总支出细项
        elif level1 == '营业总支出':
            if level2 == '管理费用':
                if level3 == '房租':
                    return '房租'
                elif level3 == '水电气费':
                    if '电费' in text_to_check:
                        return '电费'
                    elif '水费' in text_to_check:
                        return '水费'
                    elif '燃气' in text_to_check:
                        return '燃气费'
                    else:
                        return '其他水电费'
                elif level3 == '网络/手机费':
                    if '手机' in text_to_check or '话费' in text_to_check:
                        return '手机费'
                    elif '网络' in text_to_check or '宽带' in text_to_check:
                        return '网络费'
                    else:
                        return '其他通讯费'
                elif level3 == '保险/医疗':
                    if '保险' in text_to_check:
                        return '保险费'
                    elif '医疗' in text_to_check or '医院' in text_to_check:
                        return '医疗费'
                    else:
                        return '其他保险医疗费'
                elif level3 == '税费':
                    return '税费'
                elif level3 == '学习提升':
                    if '培训' in text_to_check or '课程' in text_to_check:
                        return '培训费'
                    elif '书籍' in text_to_check or '学习' in text_to_check:
                        return '学习费用'
                    else:
                        return '其他学习费用'
            
            elif level2 == '销售费用':
                if level3 == '食品':
                    if '外卖' in text_to_check:
                        return '外卖'
                    elif '超市' in text_to_check or '购物' in text_to_check:
                        return '超市购物'
                    elif '餐厅' in text_to_check or '饭店' in text_to_check:
                        return '餐厅用餐'
                    else:
                        return '其他食品'
                elif level3 == '交通':
                    if '打车' in text_to_check or '滴滴' in text_to_check:
                        return '网约车'
                    elif '地铁' in text_to_check or '公交' in text_to_check:
                        return '公共交通'
                    elif '加油' in text_to_check:
                        return '加油费'
                    else:
                        return '其他交通费'
                elif level3 == '娱乐':
                    if '游戏' in text_to_check:
                        return '游戏'
                    elif '电影' in text_to_check or '视频' in text_to_check:
                        return '影视娱乐'
                    else:
                        return '其他娱乐'
                elif level3 == '购物':
                    if '服装' in text_to_check or '衣服' in text_to_check:
                        return '服装'
                    elif '数码' in text_to_check or '电子' in text_to_check:
                        return '数码产品'
                    else:
                        return '其他购物'
                elif level3 == '旅游':
                    return '旅游'
                elif level3 == '人情往来':
                    if '份子钱' in text_to_check or '随礼' in text_to_check:
                        return '份子钱'
                    else:
                        return '其他人情往来'
            
            elif level2 == '财务费用':
                if '利息' in text_to_check:
                    return '利息'
                elif '手续费' in text_to_check:
                    return '手续费'
                else:
                    return '其他财务费用'
            
            elif level2 == '其他费用':
                if '随礼' in text_to_check or '份子钱' in text_to_check:
                    return '随礼'
                elif '罚款' in text_to_check:
                    return '罚款'
                else:
                    return '其他费用'
        
        return '未分类'
    
    def get_recent_bills(self, days: int = 20) -> List[Dict]:
        """获取最近N天的账单"""
        cutoff_date = datetime.now() - timedelta(days=days)
        recent_bills = [bill for bill in self.bills_data if bill['datetime'] >= cutoff_date]
        return sorted(recent_bills, key=lambda x: x['datetime'], reverse=True)
    
    def display_bills(self, days: int = 20) -> None:
        """显示账单数据"""
        recent_bills = self.get_recent_bills(days)
        
        if not recent_bills:
            print("没有找到最近20天的账单数据")
            return
        
        print(f"\n最近 {days} 天的账单数据 (共 {len(recent_bills)} 条记录):")
        print("=" * 120)
        
        # 准备表格数据
        table_data = []
        for bill in recent_bills:
            table_data.append([
                bill['datetime'].strftime('%Y-%m-%d %H:%M'),
                '支付宝' if bill['platform'] == 'alipay' else '微信',
                bill['counterparty'][:15] + '...' if len(bill['counterparty']) > 15 else bill['counterparty'],
                bill['description'][:20] + '...' if len(bill['description']) > 20 else bill['description'],
                '收入' if bill['type'] == 'income' else '支出',
                f"{bill['amount']:.2f}",
                bill['category_level1'],
                bill['category_level2'],
                bill['category_level3']
            ])
        
        # 使用tabulate显示表格
        headers = ['时间', '平台', '交易对方', '商品描述', '类型', '金额', '一级分类', '二级分类', '三级分类']
        print(tabulate(table_data, headers=headers, tablefmt='grid'))
        
        # 统计信息
        total_income = sum(bill['amount'] for bill in recent_bills if bill['type'] == 'income')
        total_expense = sum(bill['amount'] for bill in recent_bills if bill['type'] == 'expense')
        
        print(f"\n统计信息:")
        print(f"总收入: {total_income:.2f} 元")
        print(f"总支出: {total_expense:.2f} 元")
        print(f"净收入: {total_income - total_expense:.2f} 元")
    
    def get_failed_transactions_by_month(self) -> Dict[str, List[Dict]]:
        """获取每月交易失败的数据"""
        failed_by_month = {}
        
        for bill in self.bills_data:
            # 检查是否为失败交易
            status = bill['status'].lower()
            if any(keyword in status for keyword in ['失败', '退款', '取消', '异常', '错误']):
                month_key = bill['datetime'].strftime('%Y-%m')
                if month_key not in failed_by_month:
                    failed_by_month[month_key] = []
                failed_by_month[month_key].append(bill)
        
        return failed_by_month
    
    def get_monthly_summary(self) -> Dict[str, Dict]:
        """获取每月汇总数据"""
        monthly_data = {}
        
        for bill in self.bills_data:
            month_key = bill['datetime'].strftime('%Y-%m')
            if month_key not in monthly_data:
                monthly_data[month_key] = {
                    'total_income': 0,
                    'total_expense': 0,
                    'total_neutral': 0,
                    'platform_categories': {'alipay': {}, 'wechat': {}},
                    'count': 0
                }
            
            # 统计收支
            if bill['type'] == 'income':
                monthly_data[month_key]['total_income'] += bill['amount']
            elif bill['type'] == 'expense':
                monthly_data[month_key]['total_expense'] += bill['amount']
            elif bill['type'] == 'neutral':
                monthly_data[month_key]['total_neutral'] += bill['amount']
            
            # 统计平台分类
            platform = bill['platform']
            original_category = bill['original_category']
            if original_category not in monthly_data[month_key]['platform_categories'][platform]:
                monthly_data[month_key]['platform_categories'][platform][original_category] = {
                    'income': 0,
                    'expense': 0,
                    'neutral': 0
                }
            
            if bill['type'] == 'income':
                monthly_data[month_key]['platform_categories'][platform][original_category]['income'] += bill['amount']
            elif bill['type'] == 'expense':
                monthly_data[month_key]['platform_categories'][platform][original_category]['expense'] += bill['amount']
            elif bill['type'] == 'neutral':
                monthly_data[month_key]['platform_categories'][platform][original_category]['neutral'] += bill['amount']
            
            monthly_data[month_key]['count'] += 1
        
        return monthly_data
    
    def display_failed_transactions(self) -> None:
        """显示交易失败数据"""
        failed_by_month = self.get_failed_transactions_by_month()
        
        if not failed_by_month:
            print("没有找到交易失败的数据")
            return
        
        print("\n" + "=" * 80)
        print("交易失败数据统计")
        print("=" * 80)
        
        for month in sorted(failed_by_month.keys()):
            failed_bills = failed_by_month[month]
            print(f"\n{month}月 - 失败交易 {len(failed_bills)} 条:")
            print("-" * 60)
            
            for bill in failed_bills:
                type_text = '收入' if bill['type'] == 'income' else ('支出' if bill['type'] == 'expense' else '不计收支')
                print(f"{bill['datetime'].strftime('%Y-%m-%d %H:%M')} | "
                      f"{'支付宝' if bill['platform'] == 'alipay' else '微信'} | "
                      f"{bill['counterparty']} | "
                      f"{bill['description']} | "
                      f"{type_text} | "
                      f"{bill['amount']:.2f} | "
                      f"{bill['status']}")
    
    def display_monthly_summary(self) -> None:
        """显示每月汇总"""
        monthly_data = self.get_monthly_summary()
        
        if not monthly_data:
            print("没有找到月度数据")
            return
        
        print("\n" + "=" * 80)
        print("每月汇总统计")
        print("=" * 80)
        
        for month in sorted(monthly_data.keys()):
            data = monthly_data[month]
            print(f"\n{month}月:")
            print(f"  总记录数: {data['count']} 条")
            print(f"  总收入: {data['total_income']:.2f} 元")
            print(f"  总支出: {data['total_expense']:.2f} 元")
            print(f"  不计收支: {data['total_neutral']:.2f} 元")
            print(f"  净收入: {data['total_income'] - data['total_expense']:.2f} 元")
            
            # 显示平台分类统计
            print("  平台分类统计:")
            for platform in ['alipay', 'wechat']:
                platform_name = '支付宝' if platform == 'alipay' else '微信'
                categories = data['platform_categories'][platform]
                if categories:
                    print(f"    {platform_name}:")
                    for category, amounts in categories.items():
                        if amounts['income'] > 0 or amounts['expense'] > 0 or amounts['neutral'] > 0:
                            print(f"      {category}: 收入 {amounts['income']:.2f}, 支出 {amounts['expense']:.2f}, 不计收支 {amounts['neutral']:.2f}")
    
    def display_bills_enhanced(self, bills: List[Dict], title: str = "账单数据") -> None:
        """显示增强版账单数据"""
        if not bills:
            print(f"没有找到{title}数据")
            return
        
        print(f"\n{title} (共 {len(bills)} 条记录):")
        print("=" * 150)
        
        # 准备表格数据
        table_data = []
        for bill in bills:
            type_text = '收入' if bill['type'] == 'income' else ('支出' if bill['type'] == 'expense' else '不计收支')
            table_data.append([
                bill['datetime'].strftime('%Y-%m-%d %H:%M'),
                '支付宝' if bill['platform'] == 'alipay' else '微信',
                bill['counterparty'][:15] + '...' if len(bill['counterparty']) > 15 else bill['counterparty'],
                bill['description'][:20] + '...' if len(bill['description']) > 20 else bill['description'],
                type_text,
                f"{bill['amount']:.2f}",
                bill['original_category'][:10] + '...' if len(bill['original_category']) > 10 else bill['original_category'],
                bill['status'],
                bill['payment_method'][:10] + '...' if len(bill['payment_method']) > 10 else bill['payment_method'],
                bill['category_level1'],
                bill['category_level2'],
                bill['category_level4'],  # 直接显示四级分类
                bill['category_level4']   # 四级分类作为细项
            ])
        
        # 使用tabulate显示表格
        headers = ['时间', '平台', '交易对方', '商品描述', '类型', '金额', '原始分类', '交易状态', '支付方式', '一级分类', '二级分类', '分类细项', '具体细项']
        print(tabulate(table_data, headers=headers, tablefmt='grid'))
        
        # 统计信息
        total_income = sum(bill['amount'] for bill in bills if bill['type'] == 'income')
        total_expense = sum(bill['amount'] for bill in bills if bill['type'] == 'expense')
        total_neutral = sum(bill['amount'] for bill in bills if bill['type'] == 'neutral')
        
        print(f"\n统计信息:")
        print(f"总收入: {total_income:.2f} 元")
        print(f"总支出: {total_expense:.2f} 元")
        print(f"不计收支: {total_neutral:.2f} 元")
        print(f"净收入: {total_income - total_expense:.2f} 元")
    
    def show_menu(self) -> None:
        """显示菜单"""
        while True:
            print("\n" + "=" * 60)
            print("账单分析系统")
            print("=" * 60)
            print("1. 显示默认数据（微信和支付宝各10条）")
            print("2. 显示支付宝全部数据")
            print("3. 显示微信全部数据")
            print("4. 显示交易失败数据统计")
            print("5. 显示每月汇总统计")
            print("6. 显示4级分类汇总统计")
            print("7. 提取高频分类特征")
            print("8. 显示未分类账单明细")
            print("9. 基于模式重新分类")
            print("0. 退出程序")
            print("=" * 60)
            
            choice = input("请选择操作 (0-9): ").strip()
            
            if choice == '0':
                print("感谢使用，再见！")
                break
            elif choice == '1':
                self.display_default_data()
            elif choice == '2':
                self.display_alipay_data()
            elif choice == '3':
                self.display_wechat_data()
            elif choice == '4':
                self.display_failed_transactions()
            elif choice == '5':
                self.display_monthly_summary()
            elif choice == '6':
                self.display_category_summary()
            elif choice == '7':
                self.extract_high_frequency_patterns()
            elif choice == '8':
                self.display_unclassified_bills()
            elif choice == '9':
                self.reclassify_by_patterns()
            else:
                print("无效选择，请重新输入！")
    
    def display_default_data(self) -> None:
        """显示默认数据（微信和支付宝各10条）"""
        alipay_bills = [bill for bill in self.bills_data if bill['platform'] == 'alipay'][:10]
        wechat_bills = [bill for bill in self.bills_data if bill['platform'] == 'wechat'][:10]
        
        self.display_bills_enhanced(alipay_bills, "支付宝数据（最近10条）")
        self.display_bills_enhanced(wechat_bills, "微信数据（最近10条）")
    
    def display_alipay_data(self) -> None:
        """显示支付宝全部数据"""
        alipay_bills = [bill for bill in self.bills_data if bill['platform'] == 'alipay']
        self.display_bills_enhanced(alipay_bills, "支付宝全部数据")
    
    def display_wechat_data(self) -> None:
        """显示微信全部数据"""
        wechat_bills = [bill for bill in self.bills_data if bill['platform'] == 'wechat']
        self.display_bills_enhanced(wechat_bills, "微信全部数据")
    
    def extract_high_frequency_patterns(self) -> None:
        """提取高频分类的交易对象+时间+金额特征"""
        print("\n正在分析高频分类特征...")
        
        # 统计每个交易对象的出现频率
        counterparty_stats = {}
        for bill in self.bills_data:
            counterparty = bill['counterparty']
            if counterparty and counterparty.strip():
                if counterparty not in counterparty_stats:
                    counterparty_stats[counterparty] = {
                        'count': 0,
                        'total_amount': 0,
                        'time_patterns': set(),
                        'amount_patterns': set(),
                        'bills': []
                    }
                
                counterparty_stats[counterparty]['count'] += 1
                counterparty_stats[counterparty]['total_amount'] += bill['amount']
                counterparty_stats[counterparty]['time_patterns'].add(bill['datetime'].strftime('%H:%M'))
                counterparty_stats[counterparty]['amount_patterns'].add(round(bill['amount'], 2))
                counterparty_stats[counterparty]['bills'].append(bill)
        
        # 按频率排序，取前80%的数据
        sorted_counterparties = sorted(counterparty_stats.items(), key=lambda x: x[1]['count'], reverse=True)
        total_bills = len(self.bills_data)
        target_count = int(total_bills * 0.8)
        
        high_freq_patterns = []
        current_count = 0
        
        for counterparty, stats in sorted_counterparties:
            if current_count >= target_count:
                break
                
            # 分析时间模式
            time_pattern = "任意时间"
            if len(stats['time_patterns']) <= 3:  # 如果时间模式较少，记录具体时间
                time_pattern = "|".join(sorted(stats['time_patterns']))
            
            # 分析金额模式
            amount_pattern = "任意金额"
            if len(stats['amount_patterns']) <= 5:  # 如果金额模式较少，记录具体金额
                amounts = sorted(stats['amount_patterns'])
                if len(amounts) == 1:
                    amount_pattern = str(amounts[0])
                else:
                    amount_pattern = f"{min(amounts)}-{max(amounts)}"
            
            pattern = {
                'counterparty': counterparty,
                'time_pattern': time_pattern,
                'amount_pattern': amount_pattern,
                'count': stats['count'],
                'total_amount': stats['total_amount'],
                'suggested_classification': self._suggest_classification(stats['bills'])
            }
            
            high_freq_patterns.append(pattern)
            current_count += stats['count']
        
        # 生成可修改的配置文件
        self._generate_classification_config(high_freq_patterns)
        
        print(f"提取完成！共找到 {len(high_freq_patterns)} 个高频模式，覆盖 {current_count} 条数据 ({current_count/total_bills*100:.1f}%)")
    
    def _suggest_classification(self, bills: List[Dict]) -> str:
        """为高频交易对象建议分类"""
        if not bills:
            return "未分类"
        
        # 统计现有分类
        classification_count = {}
        for bill in bills:
            level1 = bill.get('category_level1', '未分类')
            level2 = bill.get('category_level2', '未分类')
            level3 = bill.get('category_level3', '未分类')
            level4 = bill.get('category_level4', '未分类')
            
            classification = f"{level1} > {level2} > {level3} > {level4}"
            classification_count[classification] = classification_count.get(classification, 0) + 1
        
        # 返回最常见的分类
        if classification_count:
            return max(classification_count.items(), key=lambda x: x[1])[0]
        return "未分类"
    
    def _generate_classification_config(self, patterns: List[Dict]) -> None:
        """生成分类配置文件"""
        config_file = "classification_patterns.json"
        
        import json
        
        config_data = {
            "description": "高频交易对象分类模式配置文件",
            "usage": "修改此文件中的分类建议，然后使用重新分类功能",
            "patterns": patterns
        }
        
        with open(config_file, 'w', encoding='utf-8') as f:
            json.dump(config_data, f, ensure_ascii=False, indent=2)
        
        print(f"配置文件已生成: {config_file}")
        print("您可以编辑此文件来调整分类规则，然后使用重新分类功能")
    
    def load_classification_config(self) -> Dict:
        """加载分类配置文件"""
        import json
        import os
        
        config_file = "classification_patterns.json"
        if not os.path.exists(config_file):
            print(f"配置文件 {config_file} 不存在，请先运行特征提取功能")
            return {}
        
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"加载配置文件失败: {e}")
            return {}
    
    def reclassify_by_patterns(self) -> None:
        """基于模式重新分类"""
        config = self.load_classification_config()
        if not config or 'patterns' not in config:
            print("无法加载分类配置，请先运行特征提取功能")
            return
        
        patterns = config['patterns']
        reclassified_count = 0
        
        print("开始基于模式重新分类...")
        
        for bill in self.bills_data:
            counterparty = bill['counterparty']
            time_str = bill['datetime'].strftime('%H:%M')
            amount = round(bill['amount'], 2)
            
            # 查找匹配的模式
            for pattern in patterns:
                if pattern['counterparty'] == counterparty:
                    # 检查时间模式匹配
                    time_match = True
                    if pattern['time_pattern'] != "任意时间":
                        pattern_times = pattern['time_pattern'].split('|')
                        time_match = any(time_str == pt for pt in pattern_times)
                    
                    # 检查金额模式匹配
                    amount_match = True
                    if pattern['amount_pattern'] != "任意金额":
                        if '-' in pattern['amount_pattern']:
                            # 金额范围
                            min_amt, max_amt = map(float, pattern['amount_pattern'].split('-'))
                            amount_match = min_amt <= amount <= max_amt
                        else:
                            # 具体金额
                            amount_match = amount == float(pattern['amount_pattern'])
                    
                    if time_match and amount_match:
                        # 应用建议的分类
                        suggested = pattern['suggested_classification']
                        if suggested != "未分类":
                            parts = suggested.split(' > ')
                            if len(parts) >= 4:
                                bill['category_level1'] = parts[0]
                                bill['category_level2'] = parts[1]
                                bill['category_level3'] = parts[2]
                                bill['category_level4'] = parts[3]
                                reclassified_count += 1
                        break
        
        print(f"重新分类完成！共处理 {reclassified_count} 条数据")
    
    def get_unclassified_bills(self) -> List[Dict]:
        """获取未分类的账单"""
        unclassified = []
        for bill in self.bills_data:
            if (bill.get('category_level1') == '未分类' or 
                bill.get('category_level2') == '未分类' or
                bill.get('category_level3') == '未分类' or
                bill.get('category_level4') == '未分类'):
                unclassified.append(bill)
        return unclassified
    
    def display_unclassified_bills(self) -> None:
        """显示未分类的账单"""
        unclassified = self.get_unclassified_bills()
        
        if not unclassified:
            print("所有账单都已分类！")
            return
        
        print(f"\n未分类账单明细 (共 {len(unclassified)} 条):")
        print("=" * 120)
        
        # 准备表格数据
        table_data = []
        for bill in unclassified:
            type_text = '收入' if bill['type'] == 'income' else ('支出' if bill['type'] == 'expense' else '不计收支')
            table_data.append([
                bill['datetime'].strftime('%Y-%m-%d %H:%M'),
                '支付宝' if bill['platform'] == 'alipay' else '微信',
                bill['counterparty'][:20] + '...' if len(bill['counterparty']) > 20 else bill['counterparty'],
                bill['description'][:25] + '...' if len(bill['description']) > 25 else bill['description'],
                type_text,
                f"{bill['amount']:.2f}",
                bill['original_category'][:15] + '...' if len(bill['original_category']) > 15 else bill['original_category'],
                bill['status'],
                bill['payment_method'][:10] + '...' if len(bill['payment_method']) > 10 else bill['payment_method']
            ])
        
        headers = ['时间', '平台', '交易对方', '商品描述', '类型', '金额', '原始分类', '交易状态', '支付方式']
        print(tabulate(table_data, headers=headers, tablefmt='grid'))
        
        # 统计未分类的原因
        print(f"\n未分类原因分析:")
        level1_unclassified = sum(1 for bill in unclassified if bill.get('category_level1') == '未分类')
        level2_unclassified = sum(1 for bill in unclassified if bill.get('category_level2') == '未分类')
        level3_unclassified = sum(1 for bill in unclassified if bill.get('category_level3') == '未分类')
        level4_unclassified = sum(1 for bill in unclassified if bill.get('category_level4') == '未分类')
        
        print(f"一级分类未分类: {level1_unclassified} 条")
        print(f"二级分类未分类: {level2_unclassified} 条")
        print(f"三级分类未分类: {level3_unclassified} 条")
        print(f"四级分类未分类: {level4_unclassified} 条")

    def display_category_summary(self) -> None:
        """显示4级分类汇总统计"""
        print("\n" + "=" * 80)
        print("4级分类汇总统计")
        print("=" * 80)
        
        # 按4级分类统计
        category_stats = {}
        for bill in self.bills_data:
            level1 = bill['category_level1']
            level2 = bill['category_level2']
            level3 = bill['category_level3']
            level4 = bill['category_level4']
            amount = bill['amount']
            bill_type = bill['type']
            
            # 构建分类路径
            category_path = f"{level1} > {level2} > {level3} > {level4}"
            
            if category_path not in category_stats:
                category_stats[category_path] = {
                    'income': 0,
                    'expense': 0,
                    'neutral': 0,
                    'count': 0
                }
            
            if bill_type == 'income':
                category_stats[category_path]['income'] += amount
            elif bill_type == 'expense':
                category_stats[category_path]['expense'] += amount
            elif bill_type == 'neutral':
                category_stats[category_path]['neutral'] += amount
            
            category_stats[category_path]['count'] += 1
        
        # 显示统计结果
        print("\n营业收入:")
        print("-" * 60)
        for category_path, stats in sorted(category_stats.items()):
            if '营业收入' in category_path:
                print(f"{category_path}")
                print(f"  收入: {stats['income']:.2f} 元, 支出: {stats['expense']:.2f} 元, 不计收支: {stats['neutral']:.2f} 元, 笔数: {stats['count']}")
        
        print("\n营业总支出:")
        print("-" * 60)
        for category_path, stats in sorted(category_stats.items()):
            if '营业总支出' in category_path:
                print(f"{category_path}")
                print(f"  收入: {stats['income']:.2f} 元, 支出: {stats['expense']:.2f} 元, 不计收支: {stats['neutral']:.2f} 元, 笔数: {stats['count']}")
        
        print("\n其他:")
        print("-" * 60)
        for category_path, stats in sorted(category_stats.items()):
            if '其他' in category_path:
                print(f"{category_path}")
                print(f"  收入: {stats['income']:.2f} 元, 支出: {stats['expense']:.2f} 元, 不计收支: {stats['neutral']:.2f} 元, 笔数: {stats['count']}")
        
        # 计算总计
        total_income = sum(stats['income'] for stats in category_stats.values())
        total_expense = sum(stats['expense'] for stats in category_stats.values())
        total_neutral = sum(stats['neutral'] for stats in category_stats.values())
        total_count = sum(stats['count'] for stats in category_stats.values())
        
        print(f"\n总计:")
        print(f"总收入: {total_income:.2f} 元")
        print(f"总支出: {total_expense:.2f} 元")
        print(f"不计收支: {total_neutral:.2f} 元")
        print(f"总笔数: {total_count} 笔")
        print(f"净收入: {total_income - total_expense:.2f} 元")

    def run(self) -> None:
        """运行分析程序"""
        print("开始账单分析...")
        
        # 加载账单
        self.load_bills()
        
        if not self.bills_data:
            print("没有找到有效的账单数据")
            return
        
        print(f"成功加载 {len(self.bills_data)} 条账单记录")
        
        # 分类账单
        self.classify_bills()
        
        # 显示菜单
        self.show_menu()


def main():
    """主函数"""
    analyzer = BillAnalyzer()
    analyzer.run()


if __name__ == "__main__":
    main()
